import paho.mqtt.client as mqtt,random
import time,json,os,public_lib
# 全局变量
program_restart = 0 # 程序重启标志
mqtt_exit = 0       # 结束mqtt线程使能
remsg_num = 0
zero_data = ''
textname = 0
global_message=''   # 接收的消息
connect_en = 0      # mqtt成功连接标志
Ton0 = 0            # 定时器
pub_err = 0         # 发布出错标志
mss_topic='/test'   # 订阅主题
# 文件传输变量默认参数
topic_ft = "download"   # 下载
topic_upload = "upload" # 上传
topic_fb ='feedback'    # 反馈
is_wd_en = 0            # 写入完成标志
is_send_done = 0        # 发送完成标志
is_wd_en2 = 0           # 写入完成标志
err_en = 0
file_lists = {}
remotePwd = '123'       # 远程密码
LOGGER_FILE = './snap7/snap7_log.txt'
# 上传文件
def upload_file(client, userdata, filename):
    # 文件哈希值
    file_hash,data=public_lib.cal_hash(filename)
    file_size = os.stat(filename).st_size
    # 打包数据，包括文件名、块索引和数据本身
    payload = {
        "filename": os.path.basename(filename), #文件名
        "file_hash":file_hash,                  #文件哈希值
        "wd_en":1,                              #写入标志
        "send_done":0,                          #发送完成标志
        "file_hide":0,                          #文件是否隐藏标志
        "file_size":file_size                   #文件大小
        }
    # 序列化为JSON，发送消息
    client.publish(topic_upload, json.dumps(payload))
    with open(filename, 'rb') as file:
        while True:
            data = file.read(1024*10)  # 每次读取1024字节*10
            if not data:
                payload.update({"send_done":1,"wd_en":0,})
                client.publish(topic_upload, json.dumps(payload))
                break
            client.publish(topic_upload, data)
            time.sleep(0.1)  # 为了避免发送过快，可以在每次发送之间暂停
# 写入二进制文件
def write_data(filename, data):
    with open(filename, 'ab') as file:
        file.write(data) 
# 接收消息
def on_connect(client, userdata, flags, reason_code, properties):
    global connect_en,mss_topic
    public_lib.info(LOGGER_FILE,1,'MQTT连接',f"成功连接到MQTT服务端！")
    connect_en=1
    client.subscribe(topic=mss_topic)           #订阅主题01
    client.subscribe(topic=topic_ft)            #订阅传输主题
    client.subscribe(topic=topic_fb)            #订阅反馈主题
    client.subscribe(topic=topic_upload)        #订阅上传主题
# 发布消息
def publish(topic, text):                                                          
    global client,pub_err
    try:
        client.publish(topic=topic,payload=text)
    except Exception as err :
        public_lib.info(LOGGER_FILE,0,'MQTT发布',f"发布失败！->{err}")
        pub_err=1
def on_message(client, userdata, msg):
    global remsg_num,zero_data,textname,global_message,topic_ft,topic_fb,mss_topic
    global is_wd_en,program_restart,remotePwd
    is_wd_en2=0
    is_send_done=0
    if msg.topic == mss_topic:
        global_message=msg.payload
    # 交互
    if msg.topic == topic_fb:
        try:
            # 重启
            if msg.payload.decode() == f'restart@{remotePwd}':
                program_restart=1
            # 移动文件
            if msg.payload.decode() == f'move@{remotePwd}' :
                state_move_file=public_lib.move_file('TMP')
                client.publish(topic_fb, json.dumps(state_move_file)) 
            # 清除缓存文件
            if msg.payload.decode() == f'clean@{remotePwd}' :
                clean_file=public_lib.clean_file('TMP')
                client.publish(topic_fb, json.dumps(clean_file))
            # 获取文件列表
            if msg.payload.decode() == f'get@{remotePwd}' :
                file_lists={"file_lists":public_lib.get_filePath()}
                client.publish(topic_fb, json.dumps(file_lists))
            # upload_file(client,None,'Mqtt03.py')
        except Exception as err :
            print(err)
            pass
        try:
            rec_data=json.loads(msg.payload)  
            # 上传文件情况判断
            try:
                filename=rec_data["file_name"]
                if rec_data['upload_done'] == 1 :
                    public_lib.info('./snap7/file transfer.txt',1,'文件传输',f"文件 {filename} 上传成功！")
                else:
                    public_lib.info('./snap7/file transfer.txt',1,'文件传输',f"文件 {filename} 上传失败！")
            except:
                pass
            # 删除文件
            try:
                rec_data=rec_data['del_file']
                file_lists=public_lib.get_filePath()
                for i in rec_data :
                    public_lib.remove_file(file_lists[i])
            except:
                pass
            # 上传文件
            try:
                rec_data=rec_data['upload_file']        # 要上传的文件列表
                file_lists=public_lib.get_filePath()    # 本地文件列表
                # 遍历要上传的文件列表
                for i in rec_data :
                    if i in file_lists :
                        upload_file(client,None,file_lists[i])
            except:
                pass
            pass
        except Exception as err :
            pass
    # 下载文件
    if msg.topic == topic_ft:
        try:
            data = json.loads(msg.payload)
            filename = data["filename"]
            rec_hash = data["file_hash"]
            is_wd_en2 = data["wd_en"]
            is_send_done = data["send_done"]
            is_hide_file = data["file_hide"]
            if is_wd_en2 == 1 :
                try:
                    os.remove('tmp_data')
                except:
                    pass
        except Exception as err:
            pass
        # 检查是否接收完毕len(data)
        if is_wd_en == 1 and is_send_done !=1 :
            # 重组文件
            write_data('tmp_data',msg.payload)
        # 接收完成后校验文件哈希值是否与发送端一致
        if is_send_done == 1:
            file_hash,datas=public_lib.cal_hash('tmp_data')
            if file_hash == rec_hash :
                try:
                    public_lib.info('./snap7/file transfer.txt',1,'文件传输',f"文件 {filename} 接收成功！")
                except:
                    pass
                fb={"file_name":f'{filename}',"wd_done":1}
                # 写入二进制文件
                def wb_data(datas):
                    with open(f'./TMP/{filename}', "wb") as file:
                        file.write(datas)
                try:
                    wb_data(datas)
                except Exception as err:
                    os.remove(f'./TMP/{filename}')
                    wb_data(datas)
                try:
                    if  is_hide_file == 1 :
                        os.system(f'attrib +s +h ./TMP/{filename}')
                    else:
                        os.system(f'attrib -s -h ./TMP/{filename}')
                except Exception as err:
                    print(err)
            else:
                fb={"file_name":f'{filename}',"wd_done":0}
            client.publish(topic_fb, json.dumps(fb))   
            is_wd_en=0
            os.remove('tmp_data')
        if is_wd_en2 == 1:
            is_wd_en=1
            pass
def mqtt_app(id='paho_mqtt_python', url="www.icsteam.cn", port=None, user='', password='', ssl_en=0):
    global client,connect_en,err_en
    try:
        client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,id)
        client.on_connect = on_connect
        client.on_message = on_message
        client.username_pw_set(user,password)
        if ssl_en=='1' :
            client.tls_set(
                    ca_certs='emqxsl-ca.crt'
                    )
        client.connect(url, port, 60)
        client.loop_start()
        err_en=0 
        return 0
    except Exception as err:
        if err_en == 0 :
            err_en=1
            public_lib.info(LOGGER_FILE,0,'MQTT连接',f"连接出错！（检查网络连接和参数设定是否正确）->{err}")
        return 404
def main():
    global textname,id0,url,port,user,password,ssl_en,Ton0,mss_topic,connect_en
    global topic_fb,topic_ft,topic_upload,remotePwd
    dic=public_lib.cfg_read('config.ini','UTF-8')
    items=['ID','地址','端口','是否使用加密','订阅主题','账户','密码','远程密码']
    temp_items=['paho_mqtt_python','127.0.0.1',1883,'0','sensor3','','','']
    # #随机id数
    # id_num='asdfghjkl123'
    # temp=''
    # for i in range(len(id_num)):
    #     temp+=random.choice(id_num)
    # 配置参数赋值
    for i in range(8):
        try:
            temp_items[i]=(dic[items[i]])
        except Exception as err:
            pass
    id0 = temp_items[0]         # 客户端ID
    url = temp_items[1]         # MQTT服务器地址
    port = int(temp_items[2])   # MQTT服务器端口
    ssl_en = temp_items[3]      # 是否使用加密
    mss_topic = temp_items[4]   # 客户端要订阅的主题
    user = temp_items[5]        # MQTT服务器用户名
    password = temp_items[6]    # MQTT服务器密码
    pub_topic = dic['发布主题']
    # 文件传输
    topic_ft        = f"{mss_topic}/download"
    topic_upload    = f"{mss_topic}/upload"
    topic_fb        = f'{mss_topic}/feedback'
    remotePwd       = temp_items[7] #远程密码
    firstScan=0 # 第一次扫描
    firstErr=0  # 第一次错误
    global client
    while True:
        if firstScan == 0:
            state=mqtt_app(id0,url,port,user,password,ssl_en)
            firstScan=1    
        try:
            # 断开连接，并退出程序
            if mqtt_exit == 1:
                connect_en=0
                client.disconnect()
                break
            # 错误重连
            if state == 404 :
                time.sleep(5)
                state = mqtt_app(id0,url,port,user,password,ssl_en)
                firstErr = 0
                print('MQTT连接出错，重新连接！')
            time.sleep(5)
        except Exception as err:
            if firstErr == 0 :
                firstErr = 1
                public_lib.info(LOGGER_FILE,0,'MQTT连接',f"请检查输入参数！->{err}")
            time.sleep(5)
if __name__ == '__main__':
    main()